package org.vectrics.recruiter.resume;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.log4j.Logger;
import org.apache.struts.action.ActionForm;
import org.vectrics.SystemException;
import org.vectrics.common.util.StringUtils;
import org.vectrics.hibernate.ThreadProperties;
import org.vectrics.provider.CoreServiceLocator;
import org.vectrics.recruiter.RecruitServiceLocator;
import org.vectrics.recruiter.document.CandidateDocument;
import org.vectrics.recruiter.document.CandidateDocumentCategory;
import org.vectrics.recruiter.document.CandidateDocumentIndexer;
import org.vectrics.recruiter.job.Job;
import org.vectrics.recruiter.job.JobCandidate;
import org.vectrics.recruiter.job.JobCandidateStatusType;
import org.vectrics.recruiter.job.JobSearchCriteria;
import org.vectrics.recruiter.job.JobStatusType;
import org.vectrics.recruiter.struts.admin.EmailedResumeActionForm;
import org.vectrics.recruiter.user.CandidateInfo;
import org.vectrics.recruiter.user.Resume;
import org.vectrics.recruiter.user.resume.email.EmailedResumeAttachment;
import org.vectrics.recruiter.user.resume.email.EmailedResumeInfo;
import org.vectrics.recruiter.user.resume.email.EmailedResumeInfoJobCandidate;
import org.vectrics.recruiter.user.resume.email.JobExtractor;
import org.vectrics.struts.UserBufferManager;
import org.vectrics.struts.VectricsActionData;
import org.vectrics.user.Person;
import org.vectrics.user.PhoneNumber;

public class EmailedResumeWizardHelper {
    private transient Logger log = Logger.getLogger(this.getClass());

    private Set extractedJobs = null;

    private VectricsActionData actionData = null;

    private EmailedResumeInfo info = null;

    private int maxJobCount = 15;

    public EmailedResumeWizardHelper(VectricsActionData actionData) {
        super();
        this.actionData = actionData;
        String idParam = actionData.getRequest().getParameter("emailedResumeId");
        if ((idParam == null) || (idParam.length() == 0)) {
            throw new SystemException("emailedResumeId param not specified");
        }
        info = RecruitServiceLocator.getRecruitUserService().findEmailedResume(new Long(idParam));
    }

    /**
     * execute()
     * 
     * @return the Struts forward code.
     */
    public String execute() {
        String forward = null;
        EmailedResumeActionForm appForm = null;

        ActionForm form = actionData.getForm();
        if (form == null) {
            appForm = new EmailedResumeActionForm();
        } else {
            appForm = (EmailedResumeActionForm) form;
        }

        appForm.setFromEmailAddress(info.getFromEmailAddress());
        appForm.setFromEmailName(info.getFromEmailName());
        appForm.setEmailSubject(info.getEmailSubject());
        appForm.setDateSent(CoreServiceLocator.getDateFormat().getDateFormatRoundToMinute(actionData.getLocale())
                .format(info.getDateSent()));
        appForm.setPersonId(null);
        if (info.getPerson() != null) {
            appForm.setPersonId(info.getPerson().getId().toString());
        }

        if (actionData.wasButtonPressed("nextAttach")) {
            saveAttachmentCategories(actionData, info);
            if (this.getExtractedJobs().size() > 0) {
                forward = "job";
            } else {
                forward = "success";
            }
        } else if (actionData.wasButtonPressed("dispose")) {
            this.dispose(appForm, actionData, info);
            forward = "dispose";
        } else if (actionData.wasButtonPressed("nextPersonSelect")) {
            log.info("nextPersonSelect button pressed");
            boolean personSelected = this.savePersonSelect(appForm, actionData, info);
            if (personSelected == true) {
                forward = "attachment";
            } else {
                forward = "person";
            }
        } else if (actionData.wasButtonPressed("nextPerson")) {
            this.savePersonInfo(appForm, actionData, info);
            forward = "attachment";
        } else if (actionData.wasButtonPressed("nextJob")) {
            this.saveJobSelect(appForm, actionData, info);
            forward = "success";
        } else if (actionData.wasButtonPressed("nextEval")) {
            forward = "success";
            save();
        } else if (actionData.wasButtonPressed("cancel")) {
            forward = "cancel";
        } else {
            if (info.getPerson() == null) {
                forward = "person.select";
            } else {
                forward = "success";
            }
        }
        prepareNextForm(forward, info, appForm, actionData);
        log.info("Forward = " + forward);
        return (forward);
    }

    private void dispose(EmailedResumeActionForm appForm, VectricsActionData actionData, EmailedResumeInfo info) {
        Iterator iter = info.getAttachments().iterator();
        while (iter.hasNext()) {
            EmailedResumeAttachment attachment = (EmailedResumeAttachment) iter.next();
            CandidateDocument document = attachment.getDocument();
            if (document != null) {
                CandidateDocumentIndexer indexer = new CandidateDocumentIndexer(info.getDomain());
                indexer.removeDocumentFromIndex(document);
                document.setVoided(true);
                RecruitServiceLocator.getRecruitUserService().updateCandidateDocument(document);
            }
        }
        info.setStatusCode(EmailedResumeInfo.STATUS_DISPOSED);
        RecruitServiceLocator.getRecruitUserService().updateEmailedResume(info);
        ThreadProperties.forcedCommit();
    }

    private Set getExtractedJobs() {
        if (extractedJobs == null) {
            ThreadProperties.getSession().clear();
            EmailedResumeInfo emailInfo = RecruitServiceLocator.getRecruitUserService().findEmailedResume(info.getId());
            extractedJobs = new HashSet();
            JobExtractor extractor = new JobExtractor();
            Set jobsExtractedFromSubject = extractor
                    .findTestsInText(emailInfo.getDomain(), emailInfo.getEmailSubject());
            extractedJobs.addAll(jobsExtractedFromSubject);
            Iterator iter = emailInfo.getAttachments().iterator();
            while (iter.hasNext()) {
                EmailedResumeAttachment attachment = (EmailedResumeAttachment) iter.next();
                if (attachment.getDocument().getCategory().equals(CandidateDocumentCategory.CATEGORY_COVER_LETTER)) {
                    Set jobsExtractedFromCoverLetter = extractor.findTestsInText(attachment.getDomain(), attachment
                            .getDocument().getTextContent());
                    extractedJobs.addAll(jobsExtractedFromCoverLetter);
                }
            }
        }
        if (extractedJobs.size() == 0) {
            JobSearchCriteria criteria = new JobSearchCriteria();

            criteria.addStatusTypeCode(JobStatusType.STATUS_POSTED);

            criteria.addStatusTypeCode(JobStatusType.STATUS_POSTED_PENDING);
            criteria.addDomain(info.getDomain());
            int jobCount = RecruitServiceLocator.getJobProvider().findJobCount(criteria);
            if ((jobCount > 0) && (jobCount < maxJobCount)) {
                Set jobs = new HashSet();
                jobs.addAll(RecruitServiceLocator.getJobProvider().findJobs(criteria, 0, maxJobCount - 1));
            }
        }
        return (extractedJobs);
    }

    private void prepareNextForm(String forward, EmailedResumeInfo info, EmailedResumeActionForm form,
            VectricsActionData actionData) {
        if (forward.equals("person")) {
            readPersonInfoIntoForm(form);
        } else if (forward.equals("person.select")) {
            this.findPotentialExistingCandidates(actionData, info);
        } else if (forward.equals("attachment")) {
            actionData.getRequest().setAttribute("attachments", info.getAttachments());
        } else if (forward.equals("job")) {
            actionData.getRequest().setAttribute("jobs", this.getExtractedJobs());
        } else if (forward.equals("success")) {
            Person person = info.getPerson();
            if (person != null) {
            }
            info = RecruitServiceLocator.getRecruitUserService().findEmailedResume(info.getId());
            if (info.getStatusCode().equals(EmailedResumeInfo.STATUS_NEW))
                info.setStatusCode(EmailedResumeInfo.STATUS_PROCESSED);
            RecruitServiceLocator.getRecruitUserService().updateEmailedResume(info);
            ThreadProperties.forcedCommit();
        }
    }

    private void readPersonInfoIntoForm(EmailedResumeActionForm form) {
        form.setFirstName(info.getFirstName());
        form.setLastName(info.getLastName());
        form.setMiddleName(info.getMiddleName());
        form.setPhoneNumber(info.getPhoneNumber());
        form.setFromEmailAddress(info.getFromEmailAddress());
        form.setFromEmailName(info.getFromEmailName());
    }

    /**
     * 
     * @param actionData
     * @param emailedInfo
     *            The EmailedResumeInfo containing the raw information from the
     *            email received.
     */
    private void saveAttachmentCategories(VectricsActionData actionData, EmailedResumeInfo emailedInfo) {
        Iterator<String> parameterNames = actionData.getRequest().getParameterNames().iterator();
        while (parameterNames.hasNext()) {
            String paramName = (String) parameterNames.next();
            String paramValue = actionData.getRequest().getParameter(paramName);
            if (paramName.startsWith("attach_cat_")) {
                String attachIdStr = paramName.substring(11);
                Long attachmentId = new Long(attachIdStr);
                emailedInfo = RecruitServiceLocator.getRecruitUserService().findEmailedResume(emailedInfo.getId());
                EmailedResumeAttachment attachment = RecruitServiceLocator.getRecruitUserService()
                        .findEmailedResumeAttachment(attachmentId);
                attachment.getDocument().setCategory(paramValue);
                attachment.getDocument().markForReIndexing();

                log.debug("If attachment is a resume, then save a resume to the database");
                if (attachment.getDocument().getCategory().getCode().equals("resume")) {
                    if (attachment.getResume() == null) {
                        log.debug("Attachment is a resume and no resume saved yet");
                        CandidateInfo candidateInfo = RecruitServiceLocator.getRecruitUserService().findCandidateInfo(
                                info.getPerson());
                        Resume resume = new Resume();
                        resume.setCandidateInfo(candidateInfo);
                        resume.setDomain(info.getDomain());
                        resume.setName("Resume received by email");
                        resume.setDocument(attachment.getDocument());
                        RecruitServiceLocator.getRecruitUserService().updateResume(resume);
                        attachment.setResume(resume);
                    }
                }
                RecruitServiceLocator.getRecruitUserService().updateEmailedResumeAttachment(attachment);
                RecruitServiceLocator.getRecruitUserService().updateCandidateDocument(attachment.getDocument());
            }
        }
        emailedInfo = RecruitServiceLocator.getRecruitUserService().findEmailedResume(emailedInfo.getId());
        emailedInfo.setStatusCode(EmailedResumeInfo.STATUS_PARTIAL_PROCESSED);
        RecruitServiceLocator.getRecruitUserService().updateEmailedResume(emailedInfo);
        ThreadProperties.forcedCommit();
    }

    private void savePersonInfo(EmailedResumeActionForm form, VectricsActionData actionData,
            EmailedResumeInfo emailedInfo) {

        log.debug("Save new person with fields provided by user processing emailed resume");
        if (StringUtils.isNullOrBlank(form.getLastName()))
            throw new SystemException("User did not provide last name");
        if (StringUtils.isNullOrBlank(form.getFirstName()))
            throw new SystemException("User did not provide first name");
        log.debug("form.lastName = " + form.getLastName());
        log.debug("form.firstName = " + form.getFirstName());

        Person person = new Person();
        person.setLastName(form.getLastName());
        person.setFirstName(form.getFirstName());
        person.setMiddleName(form.getMiddleName());
        person.setKnownAs(form.getKnownAs());
        person.createContactInfoIfNotExist().setHomePhone(new PhoneNumber(form.getPhoneNumber()));
        person.setLocale(CoreServiceLocator.getConfiguration().getDefaultLocale().getLanguage());
        CoreServiceLocator.getPersonService().updatePerson(person);
        ThreadProperties.forcedCommit();

        CandidateInfo candidateInfo = RecruitServiceLocator.getRecruitUserService().createCandidateInfoIfNotExist(
                person);
        RecruitServiceLocator.getRecruitUserService().updateCandidateInfo(candidateInfo);
        emailedInfo = RecruitServiceLocator.getRecruitUserService().findEmailedResume(emailedInfo.getId());

        log.debug("Save new person as selected person for emailed resume");
        emailedInfo.setLastName(form.getLastName());
        emailedInfo.setFirstName(form.getFirstName());
        if (emailedInfo.getStatusCode().equals(EmailedResumeInfo.STATUS_NEW))
            emailedInfo.setStatusCode(EmailedResumeInfo.STATUS_PARTIAL_PROCESSED);
        emailedInfo.setPerson(person);
        RecruitServiceLocator.getRecruitUserService().updateEmailedResume(emailedInfo);
        ThreadProperties.forcedCommit();

        UserBufferManager bufMgr = new UserBufferManager();
        bufMgr.addBufferedObject(actionData.getRequest(), person);
    }

    private void saveJobSelect(EmailedResumeActionForm form, VectricsActionData actionData,
            EmailedResumeInfo emailedInfo) {
        emailedInfo = RecruitServiceLocator.getRecruitUserService().findEmailedResume(emailedInfo.getId());
        CandidateInfo candidateInfo = RecruitServiceLocator.getRecruitUserService().findCandidateInfo(
                emailedInfo.getPerson());
        Iterator<String> paramNames = actionData.getRequest().getParameterNames().iterator();
        while (paramNames.hasNext()) {
            String paramName = (String) paramNames.next();
            if (paramName.startsWith("job")) {
                String jobIdStr = paramName.substring(3);
                Long jobId = new Long(jobIdStr);
                Job job = RecruitServiceLocator.getJobProvider().findJob(jobId);
                if (job == null) {
                    throw new SystemException("Could not find job for param: " + jobIdStr + "  id=" + jobId.toString());
                }
                if (emailedInfo.getPerson() != null) {
                    JobCandidate jobCandidate = job.findJobCandidate(candidateInfo);
                    if (jobCandidate == null) {
                        emailedInfo = RecruitServiceLocator.getRecruitUserService().findEmailedResume(
                                emailedInfo.getId());
                        jobCandidate = job.addJobCandidate(candidateInfo);
                        RecruitServiceLocator.getJobProvider().updateJobCandidate(jobCandidate);
                        jobCandidate.setStatus(JobCandidateStatusType.STATUS_CANDIDATE_APPLIED, ThreadProperties
                                .getPerson());
                        jobCandidate.setSource(emailedInfo.getPerson());
                        jobCandidate.setResume(emailedInfo.findAttachedResume());
                        RecruitServiceLocator.getJobProvider().updateJobCandidate(jobCandidate);

                        EmailedResumeInfoJobCandidate emailJobCandidate = new EmailedResumeInfoJobCandidate();
                        emailJobCandidate.setEmailedResumeInfo(emailedInfo);
                        emailJobCandidate.setJobCandidate(jobCandidate);
                        ThreadProperties.getSession().save(emailJobCandidate);
                    }
                }
                UserBufferManager bufMgr = new UserBufferManager();
                bufMgr.addBufferedObject(actionData.getRequest(), job);
            }
            emailedInfo = RecruitServiceLocator.getRecruitUserService().findEmailedResume(emailedInfo.getId());
            emailedInfo.setStatusCode(EmailedResumeInfo.STATUS_PROCESSED);
            RecruitServiceLocator.getRecruitUserService().updateEmailedResume(emailedInfo);
            ThreadProperties.forcedCommit();
        }
    }

    private boolean savePersonSelect(EmailedResumeActionForm form, VectricsActionData actionData,
            EmailedResumeInfo emailedInfo) {
        log.info("savePersonSelect() - called");
        ThreadProperties.getSession().clear();
        boolean personSelected = false;
        String personIdStr = form.getPersonId();
        if (personIdStr == null) {
            personIdStr = actionData.getRequest().getParameter("personId");
        }
        Person selectPerson = null;
        if ((personIdStr.trim().equals("0")) || (personIdStr == null) || (personIdStr.trim().length() == 0)) {
            log.info("Person parameter not given, assuming person information is specified.");
            selectPerson = null;
        } else {
            log.info("Person param: " + personIdStr);
            Long personId = new Long(personIdStr);
            selectPerson = CoreServiceLocator.getPersonService().findPersonById(personId);
            if (selectPerson == null) {
                throw new SystemException("Could not find person: " + personIdStr);
            }
            log.info("Person selected: " + selectPerson.getId());
            personSelected = true;
            CandidateInfo candidateInfo = RecruitServiceLocator.getRecruitUserService().findCandidateInfo(selectPerson);
            if (candidateInfo == null) {
                log.info("Creating candidate info for person, since person is a candidate now");
                candidateInfo = new CandidateInfo();
                candidateInfo.setPerson(selectPerson);
                candidateInfo.setActive(true);
                RecruitServiceLocator.getRecruitUserService().updateCandidateInfo(candidateInfo);
            }
            UserBufferManager bufMgr = new UserBufferManager();
            bufMgr.addBufferedObject(actionData.getRequest(), selectPerson);

        }
        // ThreadProperties.forcedCommit();
        emailedInfo = RecruitServiceLocator.getRecruitUserService().findEmailedResume(emailedInfo.getId());
        emailedInfo.setPerson(selectPerson);
        if (emailedInfo.getStatusCode().equals(EmailedResumeInfo.STATUS_NEW))
            emailedInfo.setStatusCode(EmailedResumeInfo.STATUS_PROCESSED);
        RecruitServiceLocator.getRecruitUserService().updateEmailedResume(emailedInfo);
        ThreadProperties.forcedCommit();
        log.info("savePersonSelect() - updated resume info object");
        return (personSelected);
    }

    private void save() {

    }

    private void prepareAttachmentForm(EmailedResumeInfo info, EmailedResumeActionForm form) {

    }

    private void preparePersonForm(EmailedResumeInfo info, EmailedResumeActionForm form) {

    }

    private void prepareJobForm(EmailedResumeInfo info, EmailedResumeActionForm form) {

    }

    private void prepareEvalForm(EmailedResumeInfo info, EmailedResumeActionForm form) {

    }

    private void findPotentialExistingCandidates(VectricsActionData actionData, EmailedResumeInfo info) {
        info = RecruitServiceLocator.getRecruitUserService().findEmailedResume(info.getId());
        Set personMatches = RecruitServiceLocator.getRecruitUserService().findPossibleCandidates(info);
        if (log.isDebugEnabled())
            log.debug("Found possible persons for e-mailed resume, count = " + personMatches.size());
        actionData.getRequest().setAttribute("existingCandidates", personMatches);
    }

}
